package com.android.tripspins.app.helper;

import android.os.AsyncTask;
import android.util.Log;

import com.android.tripspins.app.bo.Pin;
import com.android.tripspins.app.bo.Route;
import com.android.tripspins.app.bo.Segment;
import com.android.tripspins.app.bo.Trip;
import com.android.tripspins.app.interfaces.OnTaskCompleted;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

//import com.google.maps.android.ui.IconGenerator;
//import com.google.maps.android.ui.IconGenerator;

public class MapsAPIHelper {

    final int WAYPOINTS_LIMIT = 8;
    OnTaskCompleted activity;


    public MapsAPIHelper(OnTaskCompleted activity) {

        this.activity = activity;
    }

    public void getRoute(Trip trip) {
        StringBuilder sbUrl = new StringBuilder();
        sbUrl.append("http://maps.googleapis.com/maps/api/directions/json?origin=");
        sbUrl.append(trip.getPinStart().getLatitude() + ",");
        sbUrl.append(trip.getPinStart().getLongitude());
        sbUrl.append("&destination=");
        sbUrl.append(trip.getPinFinish().getLatitude() + ",");
        sbUrl.append(trip.getPinFinish().getLongitude());

        sbUrl.append("&waypoints=");
        List<LatLng> waypoints = getWaypoints(trip.getPins());

        for (LatLng latLng : waypoints) {
            sbUrl.append(latLng.latitude);
            sbUrl.append(",");
            sbUrl.append(latLng.longitude);
            sbUrl.append("%7C");
        }

        sbUrl.append("&sensor=false");

        JsonParser json = new JsonParser();
        json.execute(new String[] { sbUrl.toString() });

    }


    private List<LatLng> getWaypoints(ArrayList<Pin> pins) {
        List<LatLng> distinct = getDistinctLatLng(pins);
        int interval = Math.round(distinct.size() / (WAYPOINTS_LIMIT - 2));
        if(interval <=1) return distinct ;
        ArrayList<LatLng> waypoints = new ArrayList<LatLng>();
        for(int i = interval; i< distinct.size() -1; i += interval)
        {
            waypoints.add(distinct.get(i));
        }
        return waypoints;


    }

    private ArrayList<LatLng> getDistinctLatLng(ArrayList<Pin> pins) {
        ArrayList<LatLng> distinctLatLngs = new ArrayList<LatLng>();
        for(Pin p : pins)
        {
            if(p.getLatitude()!= 0 && p.getLongitude() != 0){
                if (!distinctLatLngs.contains(p.getLatLng()))
                    distinctLatLngs.add(p.getLatLng());
            }
        }
        return distinctLatLngs;
    }

    class JsonParser extends AsyncTask<String, Void, Route> {
        private int distance;

        @Override
        protected Route doInBackground(String... params) {
            String httpResponse = readHttpGet(params[0]);
            final Route route = new Route();
            final Segment segment = new Segment();


            try {
                final JSONObject json = new JSONObject(httpResponse);
                final JSONObject jsonRoute = json.getJSONArray("routes")
                        .getJSONObject(0);
                final JSONArray jsonLeg = jsonRoute.getJSONArray("legs");
                int numLegs = jsonLeg.length();
                int numSteps;
                for (int j = 0; j < numLegs; j++)
                {
                    JSONArray jsonSteps = jsonLeg.getJSONObject(j).getJSONArray("steps");
                    numSteps = jsonSteps.length();

                    for (int i = 0; i < numSteps; i++) {
                        JSONObject step = jsonSteps.getJSONObject(i);
                        JSONObject start = step
                                .getJSONObject("start_location");
                        LatLng position = new LatLng(start.getDouble("lat"),
                                start.getDouble("lng"));

                        segment.setPoint(position);
                        int length = step.getJSONObject("distance").getInt(
                                "value");

                        distance += length;
                        segment.setLength(length);
                        segment.setDistance(distance / 1000);
                        segment.setInstruction(step.getString("html_instructions")
                                .replaceAll("<(.*?)*>", ""));
                        // Retrieve & decode this segment's polyline and add it
                        // to
                        // the
                        // route.

                        route.addPoints(decodePolyLine(step.getJSONObject(
                                "polyline").getString("points")));
                        // Push a copy of the segment to the route
                        route.addSegment(segment.copy());

                    }

                }

                route.setCopyright(jsonRoute.getString("copyrights"));
                if (!jsonRoute.getJSONArray("warnings").isNull(0)) {
                    route.setWarning(jsonRoute.getJSONArray("warnings")
                            .getString(0));
                }

                final JSONObject jsonBoundsNE = jsonRoute.getJSONObject("bounds").getJSONObject("northeast");
                final JSONObject jsonBoundsSW = jsonRoute.getJSONObject("bounds").getJSONObject("southwest");


                LatLng boundNE = new LatLng( jsonBoundsNE.getDouble("lat"), jsonBoundsNE.getDouble("lng"));
                LatLng boundSW = new LatLng( jsonBoundsSW.getDouble("lat"), jsonBoundsSW.getDouble("lng"));

                route.addBound(boundNE);
                route.addBound(boundSW);



            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return route;
        }

        @Override
        protected void onPostExecute(Route route) {
            activity.onTaskCompleted(route);

        }

        private List<LatLng> decodePolyLine(final String encoded) {
            ArrayList<LatLng> poly = new ArrayList<LatLng>();
            int index = 0, len = encoded.length();
            int lat = 0, lng = 0;
            while (index < len) {
                int b, shift = 0, result = 0;
                do {
                    b = encoded.charAt(index++) - 63;
                    result |= (b & 0x1f) << shift;
                    shift += 5;
                } while (b >= 0x20);
                int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
                lat += dlat;
                shift = 0;
                result = 0;
                do {
                    b = encoded.charAt(index++) - 63;
                    result |= (b & 0x1f) << shift;
                    shift += 5;
                } while (b >= 0x20);
                int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
                lng += dlng;

                LatLng position = new LatLng((double) lat / 1E5,
                        (double) lng / 1E5);
                poly.add(position);
            }
            return poly;
        }

        public String readHttpGet(String url) {

            StringBuilder builder = new StringBuilder();
            HttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);

            try {
                HttpResponse response = client.execute(httpGet);
                StatusLine statusLine = response.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                if (statusCode == 200) {
                    HttpEntity entity = response.getEntity();
                    InputStream content = entity.getContent();
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(content));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }
                } else {
                    Log.e(this.getClass().toString(), "Failed to download file");
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                Log.e("caralhou", e.toString());
                e.printStackTrace();
            }
            return builder.toString();

        }
    }

    /*public static MarkerOptions getMarker(Pin p, IconGenerator iconGenerator)
    {
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.title((p.getTitle().compareTo("")==0? "-" : p.getTitle()));
        markerOptions.snippet(p.getSnippet());
        markerOptions.position(p.getLatLng());
        markerOptions.flat(true);
        markerOptions.icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon(String.valueOf(p.getId()))));
        return markerOptions;
    }*/
    public static MarkerOptions getMarker(Pin p)
    {
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.title((p.getTitle().compareTo("")==0? "-" : p.getTitle()));
        markerOptions.snippet(p.getSnippet());
        markerOptions.position(p.getLatLng());
        markerOptions.flat(true);
        return markerOptions;
    }
    public MarkerOptions getMarker(LatLng latLng)
    {
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);

        return markerOptions;
    }

}
